#pragma once
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include "log.hpp"
#include "common.hpp"
#include "inetaddr.hpp"
using namespace hllog;
using namespace hladdr;
class Socket
{
public:
    virtual void SocketOrDie() = 0;
    virtual void BindOrDie(int port) = 0;
    virtual void ListenOrDie(int backlog) = 0;
    // virtual int AcceptOrDie(InetAddr *addr) = 0;
    virtual std::shared_ptr<Socket> AcceptOrDie(InetAddr *addr) = 0;
    virtual void Close() = 0;
    virtual int Recv(std::string &massage) = 0;
    virtual int Send(const std::string &massage) = 0;
    virtual void ConnectOrDie(InetAddr &addr) = 0;

public:
    void CreateTcpServerSocket(int port, int backlog = 6)
    {
        SocketOrDie();
        BindOrDie(port);
        ListenOrDie(backlog);
    }
    void CreateTcpClientSocket()
    {
        SocketOrDie();
    }
};

class TcpSocket : public Socket
{
public:
    TcpSocket()
    {
    }
    TcpSocket(int fd) : _sockfd(fd) {}
    void SocketOrDie() override
    {
        _sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_sockfd < 0)
        {
            LOG(Level::FATAL) << "socket error";
            exit(SOCKET_ERR);
        }
        LOG(Level::DEBUG) << "socket success, sockfd : " << _sockfd;
    }
    void BindOrDie(int port) override
    {
        InetAddr addr(port);
        int n = bind(_sockfd, addr.GetInetAddr(), addr.GetLen());
        if (n < 0)
        {
            LOG(Level::FATAL) << "bind error";
            exit(SOCKET_ERR);
        }
        LOG(Level::DEBUG) << "bind success, sockfd : " << _sockfd;
    }
    void ListenOrDie(int backlog) override
    {
        int n = listen(_sockfd, backlog);
        if (n < 0)
        {
            LOG(Level::FATAL) << "listen error";
            exit(SOCKET_ERR);
        }
        LOG(Level::DEBUG) << "listen success, sockfd : " << _sockfd;
    }
    // int AcceptOrDie(InetAddr *addr) override
    // {
    //     struct sockaddr_in peer;
    //     socklen_t len = sizeof(peer);
    //     int fd = accept(_sockfd, (struct sockaddr *)&peer, &len);
    //     if (fd < 0)
    //     {
    //         LOG(Level::FATAL) << "accept error";
    //         return -1;
    //     }
    //     LOG(Level::DEBUG) << "accept success";
    //     return fd;
    // }

    std::shared_ptr<Socket> AcceptOrDie(InetAddr *addr) override
    {
        struct sockaddr_in peer;
        socklen_t len = sizeof(peer);
        int fd = accept(_sockfd, (struct sockaddr *)&peer, &len);
        if (fd < 0)
        {
            LOG(Level::FATAL) << "accept error";
            return nullptr;
        }
        LOG(Level::DEBUG) << "accept success";
        addr->Set(peer);
        return std::make_shared<TcpSocket>(fd);
    }
    void Close() override
    {
        close(_sockfd);
    }
    int Recv(std::string &massage) override
    {
        char buff[1024];
        int n = recv(_sockfd, buff, sizeof(buff), 0);
        if (n > 0)
        {
            buff[n] = '\0';
            massage += buff;
        }
        return n;
    }
    int Send(const std::string &massage) override
    {
        int n = send(_sockfd, massage.c_str(), massage.size(), 0);
        return n;
    }
    void ConnectOrDie(InetAddr &addr) override
    {
        int n = connect(_sockfd, addr.GetInetAddr(), addr.GetLen());
        if (n < 0)
        {
            LOG(Level::FATAL) << "connect error";
            exit(SOCKET_ERR);
        }
        LOG(Level::DEBUG) << "connect success, sockfd : " << _sockfd;
    }

private:
    int _sockfd;
};